package me.duzhi.ilog.cms.hok;

import com.jfinal.log.Log;
import io.jpress.utils.ClassUtils;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author ashang.peng@aliyun.com
 * @date 二月 14, 2017
 */

public class HokManager {
    private final Map<String, List<Action>> actionMap;
    public static HokManager me = new HokManager();
    private static final Log log = Log.getLog(HokManager.class);

    public HokManager() {
        actionMap = new ConcurrentHashMap<String, List<Action>>();
    }
    public boolean init = false;
    public static HokManager me() {
        if(!me.init) {
            synchronized (me) {
                if(!me.init) {
                    me.init= true;
                    me.init();
                }
            }
        }
        return me;
    }

    public void registerAction(Class<? extends Action> actionClass) {
        if (actionClass == null) {
            return;
        }
        Hok listenerAnnotation = actionClass.getAnnotation(Hok.class);
        if (listenerAnnotation == null) {
            log.warn("actionClass[" + listenerAnnotation + "] resigter fail,because not use Hok annotation.");
            return;
        }
        String[] actions = listenerAnnotation.action();
        for (String action : actions) {
            List<Action> actions1 = actionMap.get(action);
            if (actions1 == null) {
                actions1 = new ArrayList<Action>();
            }
            Action action1 = newAction(actionClass);
            actions1.add(action1);
            Collections.sort(actions1, new Comparator<Action>() {
                @Override
                public int compare(Action o1, Action o2) {
                    Hok l1 = o1.getClass().getAnnotation(Hok.class);
                    Hok l2 = o2.getClass().getAnnotation(Hok.class);
                    return l1.weight() - l2.weight();
                }
            });
            actionMap.put(action, actions1);
        }
    }

    public void unRegisterAction(Class<? extends Action> actionClass) {
        for (Map.Entry<String, List<Action>> entry : actionMap.entrySet()) {
            Action deleteAction = null;
            for (Action action : entry.getValue()) {
                if (action.getClass() == actionClass) {
                    deleteAction = action;
                }
            }
            if (deleteAction != null) {
                entry.getValue().remove(deleteAction);
            }
        }
    }

    private Action newAction(Class<? extends Action> actionClass) {
        Action action = null;
        try {
            action = actionClass.newInstance();
        } catch (Throwable e) {
            log.error(String.format("action \"%s\" newInstance is error. ", actionClass), e);
        }
        return action;
    }

    /**
     * @param action
     * @param invoke
     * @param <T>
     * @return
     */
    public <T> void invoke(HokInvoke.Message message, HokInvoke.Inv<T> invoke, String action) {
        List<Action> actions = actionMap.get(action);
        Action action1 = getHandler(actions, new DefaultAction());
        action1.handle(message, invoke, action);
    }

    public void init() {
        List<Class<Action>> list = ClassUtils.scanSubClass(Action.class,true);
        for (Class<Action> actionClass : list) {
            Hok hok = actionClass.getAnnotation(Hok.class);
            if (hok != null) {
                HokManager.me().registerAction(actionClass);
            }
        }
    }

    public static Action getHandler(List<Action> handlerList, Action action) {
        Action result = action;
        if (handlerList != null) {
            for (int i = handlerList.size() - 1; i >= 0; i--) {
                Action temp = handlerList.get(i);
                temp.next = result;
                result = temp;
            }
        }
        return result;
    }
}
